Skip to content

Conversation

@Ivopires
Copy link
Owner

I chose the List Posts by Rating challenge from the list provided (list of programming challenges).

The challenge was developed using Python(2.7), which was the most familiar programming language, and the Django(1.11.15) framework.

Although, I had never used a Python framework for web application development, I thought it would be a good opportunity to use one.

In the README, you can find all the available application endpoints, as well as all the instructions necessary to run the application and its tests.

@Mike-Sundays
Copy link

Hi Ivo. Thanks for taking the time to do the challenge.
The team will now review your pull request :)

@@ -1,2 +1,57 @@
# ListPostsByRating
App developed in Python, using the Django library
[![Build Status](https://travis-ci.com/Ivopires/ListPostsByRating.svg?branch=challenge)](https://travis-ci.com/Ivopires/ListPostsByRating)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kudos for the documentation!

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey Miguel! Thanks for the feedback on the challenge

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import numpy as np

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you feel the need to use numpy here?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided to used the numpy library in order to create more precise and consistent floats with 64-bit representation, which were used in the compute_score method implemented.


def compute_score(self):
"""
The compute_score function will compute the Wilson-score Interval,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the advantages of using this score algorithm here, as opposed to a simpler one?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main reasoning behind my decision was to solve the problem that was specified in the challenge description, where 2 posts with the same ratio (ex: 60 up and 40 down votes, and 600 up and 400 down) would have to be evaluated with a different score rather than a simple ratio between up/down votes (which could be a possible implementation of a simpler algorithm)

def __str__(self):
return self.post_text

def compute_score(self):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the application started growing and adding a lot of new functionalities, would you keep the business logic here in the models?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, I think the best solution is to implement a new class with the business logic implemented here inside the model, and then move this method to that class. And from there one, implement different classes with different purposes.


def up_vote(request, post_id):
post = get_object_or_404(Post, pk=post_id)
post.up_votes += 1

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if we get two requests at the same time to update the same post with an upvote?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I completely neglected that fact when constructing the logic behind the up/down voting actions! On that case, if two concurrent requests would upvote a post there would happen an inconsistency on the up/down vote counting (where one of them would see the counter with the old value, for example 2 up votes, when the other had already incremented the counter to 3, for example)! But to resolve that problem, one could adopt a locking system to acquire the database lock before accessing and updating the database value of the post's upvotes.



def up_vote(request, post_id):
post = get_object_or_404(Post, pk=post_id)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the right place to hold the incrementing logic?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think one of the possible solutions would be to implement the incrementing logic on an external class, although on the Django documentation the logic that lays behind the data that is presented to the user is implemented on the "view" class.

In order to gain some insight into how Django makes the split of the different layers, I checked some of the Django's documentation, including the FAQ, and some threads on the Stackoverflow (as an example stackoverflow post)

Copy link

@paulosilva86 paulosilva86 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice job Ivo. Let us ask you some questions in a code review style. Get back to us whenever you can. Take it easy with the beers 🍻 in Amsterdam 😉


def index(request):
return HttpResponse("Your posts will be posted here")
latest_posts_list = Post.objects.all()[:10]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to fetch all Posts and then filter the first 10. Am I right?
If that's true, imagine the Posts table has millions of records. What could be the issue here? How would you solve it?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Paulo, thanks for the review!

That could lead to a performance issue, but Django uses the Python's array-slicing syntax to limit the QuerySet to a certain number of results (in the code above, 10 rows), which is the equivalent to the to SQL's LIMIT and OFFSET.

At the beginning, I was also doubting about that syntax, but then after further reading of Django's documentation and other threads found that Django will use the limit clause in the SQL to fetch only the 10 rows.

post.up_votes += 1
elif vote_type == 'downvote':
post.down_votes += 1
post.save()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imagine you have 2 concurrent requests. Can you guarantee you won't lose votes with this solution? If not, how would you solve it?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this question is similar to the question made by @Mike-Sundays, which answer could be found above.

But in short, I think a race condition between two concurrent requests would arise and one plausible solution would be to use a locking mechanism that could acquire the lock to the database before accessing and updating the database values for the post's votes.

urlpatterns = [
url(r'^$', views.index, name='index'),
url(r'^(?P<pk>[0-9]+)/$', views.PostDetailView.as_view(), name='post'),
url(r'^vote/(?P<post_id>[0-9]+)/$', views.vote, name='vote'),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Ivo!
Thanks for your time!

Why do you implemented 2 ways to upvote/downvote? (vote/, upvote/, downvote/ )

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Pedro, thanks for the review!

Basically, the vote/ endpoint is responsible to handle the POST requests that are sent by the vote mechanism that was implemented in the UI, where the user could choose from up/down vote!
It was just created as an extra feature where the user naturally would navigate from the "index" endpoint to a specific post (hold by the /:post_id endpoint) and then had the possibility to up/down vote the post.


try:
vote_type = request.POST['vote']
except (KeyError, Post.DoesNotExist):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey dude! Thanks for your time!

Imagine that this application goes to production and eventually you'll have errors like this one. What would you do to be notified about exceptions/errors and it's details?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey Bernardo!

I think the more sensible way to keep up with the exceptions/erros and its details would be to integrate an error monitoring system, that could track and report the application's errors that impacted the user (like Rollbar and Sentry)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔝

@@ -1,2 +1,57 @@
# ListPostsByRating
App developed in Python, using the Django library
[![Build Status](https://travis-ci.com/Ivopires/ListPostsByRating.svg?branch=challenge)](https://travis-ci.com/Ivopires/ListPostsByRating)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gif

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants